

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <setjmp.h>
#include "mythread.h"

int cnt = 0; /* count of number of nodes in the list, not really used */

void printQueue( node_t* );

/**
 * Main function used for testing purposes
 *
 * \param none
 * \return 0 on successful completion; there are no fatal errors
 */
int main ( void ) {
	node_t *first = NULL; /* first node in the queue */
	node_t *last = NULL;  /* last node in the queue */
	
	int i = 0;
	node_t *newThread;
	newThread = (node_t *) malloc ( sizeof(node_t) );
	if (newThread == NULL) {
		printf("Unable to add new thread information; no more memory available\n");
		return 1;
	}
	
	newThread->threadId = i;
	if(newThread == NULL){
		printf("New thread pointer is null before\n");
	}
	addLast(newThread, first, last);	
	if(newThread == NULL){
		printf("New thread pointer is null after\n");
	}
	
	i++;
	node_t *newThread1 = (node_t *) malloc ( sizeof(node_t) );
	if (newThread1 == NULL) {
		printf("Unable to add new thread information; no more memory available\n");
		return 1;
	}
	
	newThread1->threadId = i;
	if(newThread1 == NULL){
		printf("New thread pointer is null before\n");
	}
	addLast(newThread1, first, last);	
	if(newThread1 == NULL){
		printf("New thread pointer is null after\n");
	}
	
	printQueue(first);
	
    return 0;

}

/**
 * Add a new node to the linked list queue, at the end
 *
 * \param An input value to be added to the list
 * \param Pointer to first thread in list
 * \param Pointer to last thread in list
 * \return nothing
*/
extern void addLast ( node_t *newThread, node_t *f, node_t *l ) {
	node_t *first = f;
	node_t *last = l;
	
	/* connect the new thread at the end of the existing list */
    newThread->next = NULL;
    newThread->prev = last;

    /* don't forget to update the head and tail of the list */
    if (last != NULL)
        last->next = newThread;
    if (first == NULL)
        first = newThread;
    last = newThread;

    cnt++;

    return;
}

/**
 * Remove a thread from the beginning of the list
 *
 * \param Pointer to first thread in list
 * \param Pointer to last thread in list
 * \return Nothing
 */
extern void removeFirst ( node_t *first, node_t *last ) {

    node_t *n;
    node_t *p;

    p = NULL;
    n = first;
    first = (*n).next;
	(*first).prev = NULL;

    free (n);  /* no memory leaks! */

    return;
}

/**
 * Move thread from the beginning of the queue to the end
 *
 * \param Thread to move to end of list
 * \param Pointer to first thread in list
 * \param Pointer to last thread in list
 * \return Nothing
 */
extern void moveFirst( node_t *thread, node_t *f, node_t *l ){
	
	node_t *first = f;
	node_t *last = l;
	
	(first->next)->prev = NULL;
	first = first->next;
	
	(last->prev)->next = thread;
	thread->prev = last;
	thread->next = NULL;
	last = thread;
	
	return;
}

/**
 * Searches the queue to find out if the currently blocked thread at the front of the queue can be unblocked
 * This basically means that it searches the list for the thread and checks to see if its status is terminated
 *
 * \param The thread to check if it can be unblocked
 * \param Pointer to first thread in list
 * \param Pointer to last thread in list
*  \return  node_t* that indicates whether thread should still be blocked: full node means no longer blocked, NULL means still blocked
*/
extern node_t * isBlocked( node_t *thread, node_t *f, node_t *l ){
	
	node_t *first = f;
	
	/* Get ID of thread that we want to check its status for termination */
	mythread_t waitingForId = thread->blockedFor;
	node_t *current = first;
	
	/* Increment list checking for the thread that the current thread was blocked for, then check its status to see if it has terminated */
	while((current->next) != NULL){
		if((current->threadId) == waitingForId){
			if((current->status) == 2){
				return current;
			} else{
				return NULL;
			}
		}
	}
	
	if((current->threadId) == waitingForId){
		if((current->status) == 2){
			return 0;
		} else{
			return NULL;
		}
	}
	
	return 0;
}

/**
 * Searches the queue to find out if the thread that the current thread is trying to join exists
 *
 * \param The thread ID to check if it exists
 * \param Pointer to first thread in list
*  \return  integer that represents whether or not the thread exists: 0 means the thread does not exist, 1 means the thread exists
*/
extern int threadExists( mythread_t target, node_t* f){
	node_t *first = f;
	
	mythread_t targetId = target;
	node_t *current = first;
	
	/* Increment list checking for the thread that we want to know if it exists */
	while((current->next) != NULL){
		if((current->threadId) == targetId){
			if((current->status) == 2){
				return 0;
			} else{
				return 1;
			}
		}
	}
	
	if((current->threadId) == targetId){
		if((current->status) == 2){
			return 0;
		} else{
			return 1;
		}
	}
	
	return 0;
}

/**
 * Prints the contents of the queue (each thread's ID), used for queue testing purposes
 *
 * \param Pointer to first thread in list
*/
void printQueue( node_t *f ){
	
	node_t *first = f;
	node_t *current = first;
	
	printf("Number of nodes is: %d\n", cnt);
	
	/* Increment list printing each thread ID */
	while(current != NULL){
		printf("Current thread: %d\n", current->threadId);
		current = current->next;
	}
}
